# 指定文件编码为UTF-8
# coding: utf-8


#
# 代码特点：
# 模块化设计：将各层实现为独立类，通过组合构建网络
# 层管理：使用OrderedDict确保正向/反向传播顺序正确
# 灵活接口：支持数值梯度和反向传播两种梯度计算方式
# 高效实现：反向传播利用链式法则高效计算梯度
# 兼容性：支持one-hot和类别索引两种标签格式
# 网络结构说明：
# Affine1: 输入层 → 隐藏层的全连接变换
# Relu1: 隐藏层的ReLU激活函数
# Affine2: 隐藏层 → 输出层的全连接变换
# SoftmaxWithLoss: 输出层激活和损失计算
# 该实现展示了深度学习框架中常见的层抽象设计思想，为构建更复杂网络奠定了基础。



"""
two_layer_net.py 功能说明：
1. 实现了一个基于层的模块化两层神经网络
2. 网络结构：输入层 → 全连接层 → ReLU激活 → 全连接层 → SoftmaxWithLoss输出
3. 主要功能包括：
   - 网络参数初始化
   - 前向传播预测
   - 损失计算
   - 准确率计算
   - 两种梯度计算方式(数值梯度和反向传播)
4. 使用OrderedDict有序字典管理网络层，确保正向/反向传播顺序正确

关键点：
- 采用面向对象的设计，将各层封装为独立类
- 使用Affine层实现全连接变换
- 使用ReLU作为隐藏层激活函数
- 使用SoftmaxWithLoss组合输出层和损失计算
- 支持数值梯度验证反向传播实现
"""

# 导入系统模块
import sys, os
# 添加父目录到系统路径
sys.path.append(os.pardir)
# 导入NumPy数值计算库
import numpy as np
# 从common.layers导入网络层类
from common.layers import *
# 从common.gradient导入数值梯度计算函数
from common.gradient import numerical_gradient
# 导入有序字典类
from collections import OrderedDict

# 定义两层神经网络类
class TwoLayerNet:

    # 初始化函数
    def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
        # 初始化权重参数
        self.params = {}
        # 第一层权重(输入层到隐藏层)
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        # 第一层偏置
        self.params['b1'] = np.zeros(hidden_size)
        # 第二层权重(隐藏层到输出层)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
        # 第二层偏置
        self.params['b2'] = np.zeros(output_size)

        # 构建网络层(使用有序字典保持层顺序)
        self.layers = OrderedDict()
        # 第一全连接层(Affine变换)
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        # ReLU激活层
        self.layers['Relu1'] = Relu()
        # 第二全连接层
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

        # 输出层(Softmax + 交叉熵损失)
        self.lastLayer = SoftmaxWithLoss()

    # 前向传播预测函数
    def predict(self, x):
        # 依次通过各层进行前向传播
        for layer in self.layers.values():
            x = layer.forward(x)
        return x

    # 损失函数计算(x:输入数据, t:监督标签)
    def loss(self, x, t):
        # 先进行预测
        y = self.predict(x)
        # 计算损失
        return self.lastLayer.forward(y, t)

    # 准确率计算函数
    def accuracy(self, x, t):
        # 获取预测结果
        y = self.predict(x)
        y = np.argmax(y, axis=1)  # 转换为类别索引
        # 处理监督标签(支持one-hot和类别索引两种格式)
        if t.ndim != 1 :
            t = np.argmax(t, axis=1)
        # 计算准确率
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy

    # 数值梯度计算(x:输入数据, t:监督标签)
    def numerical_gradient(self, x, t):
        # 定义损失函数(用于梯度计算)
        loss_W = lambda W: self.loss(x, t)

        # 计算各参数的数值梯度
        grads = {}
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])

        return grads

    # 反向传播梯度计算(高效实现)
    def gradient(self, x, t):
        # 前向传播计算损失
        self.loss(x, t)

        # 反向传播
        dout = 1  # 初始梯度
        # 从输出层开始反向传播
        dout = self.lastLayer.backward(dout)

        # 反转网络层顺序进行反向传播
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # 从各层获取梯度
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads
